#!/usr/bin/env -S awk -f
# POSIX awk
# -----------
# About awk distribution:
# [[Name, Compatible]; [oawk, "x ?"] [mawk, ✓] ["gawk(gnu awk)", ✓] [nawk, "✓ ?"] ["busybox awk", ✓]]
#
# ╭───┬───────────────┬────────────╮
# │ # │     Name      │ Compatible │
# ├───┼───────────────┼────────────┤
# │ 0 │ oawk          │ x ?        │
# │ 1 │ mawk          │ ✓          │
# │ 2 │ gawk(gnu awk) │ ✓          │
# │ 3 │ nawk          │ ✓ ?        │
# │ 4 │ busybox awk   │ ✓          │
# ╰───┴───────────────┴────────────╯
#
# On some systems, awk refers to oawk(1977).
# In fact, awk in the 1980s was very different from the 1970s.
# In this case, you should call (mawk, nawk, gawk or busybox awk) manually, e.g. nawk -f this-file.awk
# Or you can modify the shebang(line: 1)
#
# For compatibility, no gawk extensions have been introduced here.
#
# Although it is tested with mawk(1.3.4 20200120), theoretically any awk compatible with the new POSIX will work.
# Unfortunately, this version of mawk has some bugs.
# Even busybox awk doesn't have those bugs.
# Fortunately, you can still run it.
#
# Due to the special local variable declaration syntax of awk, if you find "_," in the declaration of a function, then don't panic.
# After this, the parameters(variables) are local.
# If the norm is followed, it should not be "_", but some spaces.
# -----------
function main() {
    set_const()
    get_system_info()
    add_repo()
    install_init()
}
# -----------
#[test]
function test() {
    set_const()
    # test_cmd_exists()
    # test_check_cmd()
    # test_alice_s_fruit()
    test_file_existence()
}
# -----------
# Get system information.
#
# In this world, there exist are many operating systems.
# For example:
# [[OS]; ["Microsoft Windows"], ["Apple MacOS"], ["Linux distros"], ["*BSD$"], ["Redox"]]
#
# Q: Windows?
#    Are you serious?
# A: Yes, windows can also run awk.
#    As windows 8.1/10/11(22000.856) do not have `awk` pre-installed, it may be better to use other methods.
#
# Note: windows and mac are not supported at this time.
# But maybe one day I'll get it to support mac.
#
# ╭───┬───────────────╮
# │ # │      OS       │
# ├───┼───────────────┤
# │ 0 │ Windows       │
# │ 1 │ MacOS         │
# │ 2 │ Linux distros │
# │ 3 │ *BSD$         │
# │ 4 │ Redox         │
# ╰───┴───────────────╯
#
function get_system_info() {
    uname_cmd = check_uname_cmd()

    kernel_name = get_cmd_out(uname_cmd ",-s")

    # get os name and set OS["name"], OS["family"], etc.
    get_os_name(uname_cmd, kernel_name)

    # set I18N["lang"], I18N["region"], etc.
    get_os_language()

    # set ARCH["deb"] and ARCH["llvm"]
    # To be honest, there is little use in getting the architecture before the edition 2022 is completed.
    # There is always a need to look ahead and there is nothing wrong with being prepared in advance.
    get_architecture(uname_cmd)
}
# -----------
function check_uname_cmd(_, uname_cmd) {
    uname_cmd = (check_cmd("uname"))? "uname":      (check_cmd("busybox", "uname,-s"))? "busybox uname": NONE

    # [[OS, pkg]; ["Debian GNU/Linux", coreutils] [Windows, busybox]] 
    if (is_empty(uname_cmd)) {

        print sty["cyan"]
        print "╭───┬──────────────────┬───────────╮" 
        print "│ # │       OS         │    pkg    │" 
        print "├───┼──────────────────┼───────────┤" 
        print "│ 0 │ Debian GNU/Linux │ coreutils │" 
        print "│ 1 │ Windows          │ busybox   │" 
        print "╰───┴──────────────────┴───────────╯" sty[0]


        print sty["green"] "`uname`", sty["yellow"] "is used to detect kernel information.\n",
        sty["blue"] "For Linux distros such as", sty["magenta"] "Debian GNU/Linux" sty[0] ",", sty["cyan"] "you can install", sty["green"] "`coreutils`.\n",
        sty["yellow"] "For", sty["blue"] "Windows" sty[0] ",", sty["cyan"] "you can use", sty["green"] "busybox.\n" sty[0]
        exit 127
    }

    return uname_cmd
}
# -----------
# Get OS name
#
# Example
#
# ```awk
#     uu = "uname"
#     
#     kernel = get_cmd_out(uu ",-s")
#     get_os_name(uu, kernel)
#
#     print OS["name"], OS["family"]
# ```
#
function get_os_name(uname_cmd, kernel_name, _, k, os_name) {
#  [[OS, "uname -a"]; ["Windows", "Windows_NT MOE 10.0 22000 x86_64 MS/Windows"] ["Debian GNU/Linux", "Linux nuc 5.18.0-3-amd64 #1 SMP PREEMPT_DYNAMIC Debian 5.18.14-1 (2022-07-23) x86_64 GNU/Linux"]]
#
# ╭───┬──────────────────┬────────────────────────────────────╮
# │ # │        OS        │              uname -a              │
# ├───┼──────────────────┼────────────────────────────────────┤
# │ 0 │ Windows          │ Windows_NT MOE 10.0 22000 x86_64   │
# │   │                  │ MS/Windows                         │
# │ 1 │ Debian GNU/Linux │ Linux nuc 5.18.0-3-amd64 #1 SMP    │
# │   │                  │ PREEMPT_DYNAMIC Debian 5.18.14-1   │
# │   │                  │ (2022-07-23) x86_64 GNU/Linux      │
# ╰───┴──────────────────┴────────────────────────────────────╯
#
# [[OS,"Kernel Name"]; [linux, Linux] [mac, Darwin] [win, "Windows_NT|MINGW.*|MSYS.*|CYGWIN.*"] [freebsd, FreeBSD] [netbsd, NetBSD] [dragonfly, DragonFly] [sunos, SunOS]]
#
# ╭───┬───────────┬────────────────────────────────────╮
# │ # │    OS     │            Kernel Name             │
# ├───┼───────────┼────────────────────────────────────┤
# │ 0 │ linux     │ Linux                              │
# │ 1 │ mac       │ Darwin                             │
# │ 2 │ win       │ Windows_NT|MINGW.*|MSYS.*|CYGWIN.* │
# │ 3 │ freebsd   │ FreeBSD                            │
# │ 4 │ netbsd    │ NetBSD                             │
# │ 5 │ dragonfly │ DragonFly                          │
# │ 6 │ sunos     │ SunOS                              │
# ╰───┴───────────┴────────────────────────────────────╯
#
    # to lower case
    k = tolower(kernel_name)

    # os_name = (k ~ /linux/)?
    #         "linux":
    # (k ~ /darwin/)?
    #         "mac":
    # (k ~ /windows_nt|cygwin|m(ingw|sys)/)?
    #         "win":
    # (k ~ /dragonfly|(net|free)bsd|sunos/)?
    #         k:
    #     NONE
    # Warning: mawk(1.3.4 20200120) uses the above statement with a "syntax error at or near :", but busybox awk does not.
    # It needs to be combined into one line.

    if (k ~ /linux/)
        os_name = "linux"
    else if (k ~ /darwin/)
        os_name = "mac"
    else if (k ~ /windows_nt|cygwin|m(ingw|sys)/)
        os_name = "win"
    else if (k ~ /dragonfly|(net|free)bsd|sunos/)
        os_name = k
    
    if (is_empty(os_name)) {
        print sty["red"] "Unfortunately,", sty["yellow"] "it does", sty["magenta"] "not support", sty["blue"] "your system.", "\n",
        sty["cyan"] "kernel name:", sty["green"] kernel_name, "\n",
        sty["blue"] "Welcome to report this issue" sty[0]

        exit 1
    }

    # OS["family"] = "ubuntu"
    # OS["family2"] = "debian"
    # OS["env"] = gnu / musl

    OS["target-family"] = (os_name == "win")? "windows": "unix"
    
    OS["kernel"] = kernel_name
    OS["name"] = OS["family0"] = os_name
    parse_os_name(uname_cmd, os_name, OS)

    #[test]
    # for (i in OS)
    #     print "debug::get_os_name, key:", i, "v:", OS[i]
}
# -----------
# parse_os_name(os_name: string, os: array)
function parse_os_name(uname_cmd, os_name, os, _, os_lower, release, release_file) {
    # TODO: set OS["version"] & OS["code"]
    # debian: OS["version"] = 12, OS["code"] = "bookworm"
    # windows: OS["version"] = 11, OS["full_version"] = "22000.856"

    if (os_name ~ /sunos|linux/) {
        release_file = "/etc/os-release"
        os_lower = tolower(get_cmd_out(uname_cmd ",-o"))

        os["name"] = (os_lower == "android")? "android":    (os_lower == "illumos")? "illum": os_name
    }

    if (os_name == "linux" && is_file_exists(release_file)) {
        collect_to_arr(release_file, "=", release)
        #[test]
        # for (i in release)
        #     print "k:", i, "v:", release[i]
        # print dequotes(release["PRETTY_NAME"])

        if ("ID" in release)
            os["name"] = os["family"] = dequotes(release["ID"])
        # [["OS", ID_LIKE]; [kali, debian] [mint, ubuntu] [lmde, debian]]
        #
        # ╭───┬──────┬─────────╮
        # │ # │  OS  │ ID_LIKE │
        # ├───┼──────┼─────────┤
        # │ 0 │ kali │ debian  │
        # │ 1 │ mint │ ubuntu  │
        # │ 2 │ lmde │ debian  │
        # ╰───┴──────┴─────────╯
        #
        if ("ID_LIKE" in release)
            os["family2"] = dequotes(release["ID_LIKE"])
    }
}
# -----------
# Please do not declare `lang` local variables in this function
# It comes from the user passed in `-v lang=xx-yy`
#
function get_os_language(_, cmd, full, language) {
    cmd = "getprop"

    # Default locale: en_us
    I18N["lang"] = "en"
    I18N["region"] = "us"

    # `lang` from user incoming data
    # awk -f a.awk -v lang=en_us
    if (!is_empty(lang) && lang ~ /[_-]/)
        full = lang
    else if (OS["name"] == "android" && check_cmd(cmd))
        full = get_cmd_out(cmd ",persist.sys.locale")
    else if (OS["name"] == "win")
        full = get_cmd_out("powershell,-NoProfile,'Get-UICulture|select -ExpandProperty Name'")
    else
        full = ENVIRON["LANG"]

    language = trim(tolower(full))

    if (!is_empty(language))
        parse_os_language(language, I18N)
    
    #[test]
    # key: lang v: en
    # key: region v: us
    # key: encoding v: utf-8
    # a.awk -v lang=zh-cmn_hans_cn.utf-8
    # key: script     v: hans
    # key: ext        v: cmn
    # key: lang       v: zh
    # key: region     v: cn
    # key: encoding   v: utf-8
    #
    # for (i in I18N) {
    #     # print "debug::get_os_language",
    #     print "key:", i, "\tv:", I18N[i]
    # }
}
# -----------
# 
# [[src, result]; ["en_us.utf-8", 'lang: en, region: us, encoding: utf-8'],  ["en_us", 'lang: en, region: us'], ["en-US", 'lang: en, region: us'], ["zh-Hans-CN", 'lang: zh, script: hans, region: cn'] ["zh-yue-Hant_hk", 'lang: zh, ext: yue, script: hant, region: hk']]
#
# ╭───┬────────────────┬──────────────────────────────────────────────╮
# │ # │      src       │                    result                    │
# ├───┼────────────────┼──────────────────────────────────────────────┤
# │ 0 │ en_us.utf-8    │ lang: en, region: us, encoding: utf-8        │
# │ 1 │ en_us          │ lang: en, region: us                         │
# │ 2 │ en-US          │ lang: en, region: us                         │
# │ 3 │ zh-Hans-CN     │ lang: zh, script: hans, region: cn           │
# │ 4 │ zh-yue-Hant_hk │ lang: zh, ext: yue, script: hant, region: hk │
# ╰───┴────────────────┴──────────────────────────────────────────────╯
#
# parse_os_language(lang: string, i18n: array)
function parse_os_language(lang, i18n, _, lang_arr1, lang_arr1_len, lang_arr2, lang_arr2_len, lang_spec, v) {
    lang_arr1_len = split(lang, lang_arr1, ".")

    if (lang_arr1_len == 2)
        i18n["encoding"] = lang_arr1[2]

    lang_arr2_len = split(lang_arr1[1], lang_arr2, /[_-]/)
    
    i18n["lang"] = lang_arr2[1]

    # language-extlang-script-region-variant-extension-privateuse
    # [[Type, Construction, Standard]; [language, "(2 or 3) alpha[a-z]", "ISO639-[1,2,3,5]"], [extlang, "3 alpha", ISO639-3], [script, "4 alpha[A-Z]{1}[a-z]{3}", ISO15924], [region, "2 alpha[A-Z] or 3 digit", "ISO3166-1_alpha-2 or UNM.49"], [variant, "([5-8] alphanum) or (1 digit  3 alphanum)", "IANA language subtag registry?"]]
    # https://www.rfc-editor.org/info/bcp47
    # https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
#
# ╭───┬──────────┬──────────────────────────┬─────────────────────────╮
# │ # │   Type   │       Construction       │        Standard         │
# ├───┼──────────┼──────────────────────────┼─────────────────────────┤
# │ 0 │ language │ (2 or 3) alpha           │ ISO639-[1,2,3,5]        │
# │ 1 │ extlang  │ 3 alpha                  │ ISO639-3                │
# │ 2 │ script   │ 4 alpha                  │ ISO15924                │
# │ 3 │ region   │ 2 alpha or 3 digit       │ ISO3166-1_alpha-2 or    │
# │   │          │                          │ UNM.49                  │
# │ 4 │ variant  │ ([5-8] alphanum) or (1   │ IANA language subtag    │
# │   │          │ digit  3 alphanum)       │ registry?               │
# ╰───┴──────────┴──────────────────────────┴─────────────────────────╯

    # Language specification array
    split("lang,ext,script,region,variant,extension,privateuse", lang_spec, ",")

    # Note: Only the first 4 types are parsed. Since the first one is always lang, only [ext, script, region] are parsed next.
    # The hashmap or array of region code(UNM.49) is not defined in this awk program, so it does not automatically convert "UNM.49" to "ISO3166-1_alpha-2"

    for (i = 2; i <= lang_arr2_len; ++i) {
        v = lang_arr2[i]
        is_i18n_extlang(v, i18n) || is_i18n_script(v, i18n) || is_i18n_region(v, i18n)
    }

    # Do not use `(! "script" in i18n)` 
    if (("region" in i18n) && (i18n["lang"] == "zh") && (is_empty(i18n["script"])))
        i18n["script"] = (i18n["region"] ~ /cn|sg|my/)? "hans": "hant"
}
# -----------
# Warning: mawk (1.3.4 20200120) uses such regular expression (/[0-9a-z]{5,8}/) with error 

function is_i18n_region(two_a_or_3d, a_map) {
    # [a-z]{2} | [0-9]{3}
    if (two_a_or_3d ~ /^[a-z][a-z]$|^[0-9][0-9][0-9]$/){
        a_map["region"] = two_a_or_3d
        return true
    }
    return false
}
function is_i18n_script(four_a, a_map){
    # [a-z]{4}
    if (four_a ~ /^[a-z][a-z][a-z][a-z]$/){
        a_map["script"] = four_a
        return true
    }
    return false
}
function is_i18n_extlang(three_a, a_map){
    # [a-z]{3}
    if (three_a ~ /^[a-z][a-z][a-z]$/){
        a_map["ext"] = three_a
        return true
    }
    return false
}
# -----------
function add_repo(_, gpgv_version) {
    if ((OS["family2"] ~ /debian|ubuntu/ || OS["family" == "debian"]) && check_cmd("gpgv")) {
        # [[sysver, "gpgv version"]; [bionic, "2.2.4-1ubuntu1.6"] [buster, "2.2.12-1+deb10u2"]]
        #
        # ╭───┬────────┬──────────────────╮
        # │ # │ sysver │   gpgv version   │
        # ├───┼────────┼──────────────────┤
        # │ 0 │ bionic │ 2.2.4-1ubuntu1.6 │
        # │ 1 │ buster │ 2.2.12-1+deb10u2 │
        # ╰───┴────────┴──────────────────╯
        #
        gpgv_version = parse_cmd_out("dpkg-query,-s,gpgv", "^Version", ":" SPACE)
        if (semver_comparator(gpgv_version, "<", "2.0.0")) {
            # TODO: if I18N["lang"] == "zh", then print Chinese
            print sty["cyan"] "gpgv version :", sty["green"] gpgv_version,
            sty["red"] "\nSorry,", sty["blue"] "the gpgv version is lower than", sty["yellow"] "2.0",
            sty["cyan"] "\nSupport for", sty["underline"] sty["yellow"] "Curve25519/Ed25519",sty["not underlined"] sty["magenta"] "may not be perfect" sty[0] "\n"

            print "Warning! Your system will not be able to add the related repo" > STDERR
        }
    }

    # TODO: download deb file and install pkg
    # [[Step, Command]; ["download deb", "curl -LO https://l.tmoe.me/neko.deb"] ["install deb as root", "sudo apt install ./neko.deb"] ["update index", "sudo apt update"] ["install tmm", "sudo apt install tmm"]]
#
# ╭───┬─────────────────────┬─────────────────────────────────────╮
# │ # │        Step         │               Command               │
# ├───┼─────────────────────┼─────────────────────────────────────┤
# │ 0 │ download deb        │ curl -LO https://l.tmoe.me/neko.deb │
# │ 1 │ install deb as root │ sudo apt install ./neko.deb         │
# │ 2 │ update  index       │ sudo apt update                     │
# ╰───┴─────────────────────┴─────────────────────────────────────╯

}
# -----------
# Comparison of version numbers.
#
# Example

# ```awk
#     a = "1.10.0"
#     b = "1.9.2"
#     op = ">"
#     aa = semver_comparator(a, op, b)

#     print_bool(aa)
#     # stdout: true

#     bb = semver_comparator("1.5.12-alpha.2", "<", "1.5.12-beta.1")
#     if (bb)
#         print "1.5.12-alpha.2 is lower than 1.5.12-beta.1"
# ```
# semver_comparator(lhs: string, op: string, rhs: string) -> bool
function semver_comparator(lhs, op, rhs, _, m_arr, m_arr_len, arr_len, n_arr, n_arr_len, i) {
    m_arr_len = split(lhs , m_arr, "[-.]")
    n_arr_len = split(rhs, n_arr, "[-.]")
    arr_len = (m_arr_len > n_arr_len)? m_arr_len: n_arr_len

    for (i = 1; i <= arr_len; ++i) {

        m_arr["op"] = (m_arr[i] > n_arr[i])? ">": (m_arr[i] < n_arr[i])? "<": "="

        if (m_arr["op"] != "=")
            break
    }

    op_clone = op
    sub("=", NONE, op_clone)

    #[test]
    # print a_arr["op"], op, op_clone
    return (op == m_arr["op"])? true: (op_clone == m_arr["op"])? true: false
}
# -----------
function set_git_uri(git, uri, _, a_map, opt) {
    git["ee"] = "gid5564/aidboot"
    git["branch"] = "master"
    git["install"] = "install.sh"

    uri["branch"] = git["branch"] "/"
    uri["protocol"] = "https://"

    uri["ee_base"] = "gitee.com/"
    uri["ee_raw"] = "/raw/"

    uri["gitee"] = uri["protocol"] uri["ee_base"] git["ee"] uri["ee_raw"] uri["branch"] git["install"]

    uri["github"] = uri["gitee"]


    y_n = coloured_y_n(true)

    # mawk -f ./a.awk -v net_node=gh => net_node 
    if (is_empty(net_node)) {
        # print I18N["region"]
        print sty["_"] "gitee" sty["/_"] " or " sty["_"] "github" sty["/_"] ":"
        if (I18N["region"] ~ /cn|tw|hk|mo/) {
                a_map["ext", "zh"] = "script"
                a_map["fallback", "zh"] = "hans"
                a_map["choose_gitee", "zh" "hans"] = "直接按回车键选择 gitee"
                a_map["choose_gitee", "zh" "hant"] = "直接按回車鍵選擇 gitee"

                a_map["choose_github", "zh" "hans"] = "输入n 再按回车选择 github"
                a_map["choose_github", "zh" "hant"] = "輸入n 再按回車選擇 github"
            # get_i18n("choose_gitee", a_map)

            # ╭───┬───────┬────────┬───────────────────────────╮
            # │ # │ Opt   │  Src   │      Description          │
            # ├───┼───────┼────────┼───────────────────────────┤
            # │ 0 │ y     │ gitee  │ 直接按回車鍵選擇 gitee    │
            # │ 1 │ n     │ github │ 輸入n 再按回車選擇 github │
            # ╰───┴───────┴────────┴───────────────────────────╯
            # 地區: hk/mo/tw
            # 是否從 gitee 獲取相關檔案? [Y/n]

            # ╭───┬───────┬────────┬───────────────────────────╮
            # │ # │ Opt   │  Src   │      Description          │
            # ├───┼───────┼────────┼───────────────────────────┤
            # │ 0 │ y     │ gitee  │ 直接按回车键选择 gitee    │
            # │ 1 │ n     │ github │ 输入n 再按回车选择 github │
            # ╰───┴───────┴────────┴───────────────────────────╯

            # 地区: cn
            # 是否从 gitee 获取相关文件? [Y/n]

            print sty["cyan"] "╭───┬" sty["blue"]    "──────" sty ["cyan"]    "─┬─" sty["cyan"]    "──────" sty["cyan"] "─┬" sty["yellow"]   "───────────────────────────" sty["yellow"]   "╮"
            print sty["cyan"] "│ # │" sty["blue"]    " Opt  " sty ["cyan"]    " │ " sty["cyan"]    " Src  " sty["cyan"] " │" sty["yellow"]     "      Description          " sty["yellow"]   "│"
            print sty["cyan"] "├───┼" sty["blue"]    "──────" sty ["cyan"]    "─┼─" sty["cyan"]    "──────" sty["cyan"] "─┼" sty["yellow"]   "───────────────────────────" sty["yellow"]   "┤"
            print sty["cyan"] "│ 0 │" sty["green"]   " y    " sty ["cyan"]    " │ " sty["green"]   "gitee " sty["cyan"] " │" sty["green"]    " "get_i18n("choose_gitee", a_map)"    " sty["yellow"]   "│"
            print sty["cyan"] "│ 1 │" sty["magenta"] " n    " sty ["cyan"]    " │ " sty["magenta"] "github" sty["cyan"] " │" sty["magenta"]  " "get_i18n("choose_github", a_map)" " sty["yellow"]   "│"
            print sty["cyan"] "╰───┴" sty["blue"]    "──────" sty ["cyan"]    "─┴─" sty["cyan"]    "──────" sty["cyan"] "─┴" sty["yellow"]   "───────────────────────────" sty["yellow"]   "╯"
            print sty[0]

            a_map["region", "zh" "hans"] = "地区"
            a_map["region", "zh" "hant"] = "地區"

            a_map["from", "zh" "hans"] = "是否从"
            a_map["from", "zh" "hant"] = "是否從"

            a_map["get_file", "zh" "hans"] = "获取相关文件"
            a_map["get_file", "zh" "hant"] = "獲取相關檔案"

            # print get_i18n("region", a_map)

            print sty["yl"] get_i18n("region", a_map) ": " sty["b"] sty["green"] I18N["region"] sty["/b"]

            print sty["bu"] get_i18n("from", a_map) SPACE sty["u"] sty["cyan"] "gitee" sty[0]  sty["yellow"] SPACE get_i18n("get_file", a_map) sty["default"] "?", y_n
            
            print sty["bu"] "Do you want to" sty["yl"] " get it" sty["bu"] " from " sty["u"] sty["cyan"]"gitee" sty[0] "?", y_n
            opt = tolower(read_to_string(STDIN))
            uri["current"] = (opt ~ /^n/)? uri["github"]: uri["gitee"]
        } else {
            # [[Input, Src, Description]; ["y", github, "Press Enter or type y to choose github"] ["n", gitee, "Type n to choose gitee"]]
            # ╭───┬───────┬────────┬────────────────────────────────────────╮
            # │ # │ Opt   │  Src   │              Description               │
            # ├───┼───────┼────────┼────────────────────────────────────────┤
            # │ 0 │ y     │ github │ Press Enter or type y to choose github │
            # │ 1 │ n     │ gitee  │ Type n to choose gitee                 │
            # ╰───┴───────┴────────┴────────────────────────────────────────╯
            #
            print sty["cyan"] "╭───┬" sty["blue"]     "───────" sty["cyan"]     "┬" sty["cyan"]  "────────" sty["yellow"] "┬" sty["yellow"] "────────────────────────────────────────" sty["yellow"] "╮"
            print sty["cyan"] "│ # │" sty["blue"]     " Opt   " sty["cyan"]     "│" sty["cyan"]  "  Src   " sty["yellow"] "│" sty["yellow"] "              Description               " sty["yellow"] "│"
            print sty["cyan"] "├───┼" sty["blue"]     "───────" sty["cyan"]     "┼" sty["cyan"]  "────────" sty["yellow"] "┼" sty["yellow"] "────────────────────────────────────────" sty["yellow"] "┤"
            print sty["cyan"] "│ 0 │" sty["green"]    " y     " sty["cyan"]    "│" sty["green"]  " github " sty["yellow"] "│"  sty["green"] " Press Enter or type y to choose github " sty["yellow"] "│"
            print sty["cyan"] "│ 1 │" sty["magenta"]  " n     " sty["cyan"]  "│" sty["magenta"]  " gitee  " sty["yellow"] "│"sty["magenta"] " Type n to choose gitee                 " sty["yellow"] "│"
            print sty["cyan"] "╰───┴" sty["blue"]     "───────" sty["cyan"]     "┴" sty["cyan"]  "────────" sty["yellow"] "┴" sty["yellow"] "────────────────────────────────────────" sty["yellow"] "╯"
            print sty[0] "Do you want to" sty["yellow"] " get it" sty["blue"] " from " sty["underline"] sty["cyan"]"github" sty[0] "?", y_n
            opt = read_to_string(STDIN)
            uri["current"] = (opt ~ /^n/)? uri["gitee"]: uri["github"]
        }
    } else {
        uri["current"] = (net_node ~ /github|hub|gh/)? uri["github"]: uri["gitee"]
    }

    # uri["num_of_fallback"] = 1
    uri["fallback"] = uri["gitee"]
    
}
# -----------
# This function was written before I wrote `get_i18n()`.
# To rewrite it, call `get_i18n()`
function about_y_or_n(_, amap) {
    # Not yet ready to support i18n, so only `en` and `zh` are available.
    #
    # amap["option", "en"] = "Options"
    amap["option", "zh", "hans"] = "选项"
    amap["option", "zh", "hant"] = "選項"

    # amap["desc", "en"] = "Description"
    amap["desc", "zh", "hans"] = amap["desc", "zh", "hant"] = "描述"

    # amap["default_is_yes", "en"] = "Default is yes"
    # amap["default_is_no", "en"] = "Default is no"

    amap["default_is_yes", "zh", "hans"] = "默认为yes"
    amap["default_is_yes", "zh", "hant"] = "默認爲yes"

    amap["default_is_no", "zh", "hans"] = "默认为no"
    amap["default_is_no", "zh", "hant"] = "默認爲no"

    # amap["press_enter", "en"] = "Press Enter"
    # amap["type_n_press_enter", "en"] = "Type n and press Enter"
    # amap["type_y_press_enter", "en"] = "Type y and press Enter"

    amap["press_enter", "zh", "hans"] = "直接按回车键"
    amap["type_n_press_enter", "zh", "hans"] = "先输入n,再按回车"
    amap["type_y_press_enter", "zh", "hans"] = "先输入y,再按回车"

    amap["press_enter", "zh", "hant"] = "直接按回車鍵"
    amap["type_n_press_enter", "zh", "hant"] = "先輸入n,再按回車"
    amap["type_y_press_enter", "zh", "hant"] = "先輸入y,再按回車"

    # style:
    # [[index, A, B, C] [cn, blue, cyan, yellow]]
    # [[Option, Description, yes, no]; ["[Y/n]","Default is yes", "Press Enter", "Type n and press Enter" ] , ["[y/N]","Default is no", "Type y and press Enter", "Press Enter"]]
    # [[选项, 描述, yes, no]; ["[Y/n]","默认是yes", "直接按回车键", "先输入n,再按回车" ] , ["[y/N]","默认是no", "先输入y,再按回车", "直接按回车键"]]
    # amap["type_n_press_enter", I18N["lang"], I18N["script"]]
    #
    # ╭───┬────────┬────────────────┬────────────────────────┬────────────────────────╮
    # │ # │ Options│  Description   │          yes           │           no           │
    # ├───┼────────┼────────────────┼────────────────────────┼────────────────────────┤
    # │ 0 │ [Y/n]  │ Default is yes │ Press Enter            │ Type n and press Enter │
    # │ 1 │ [y/N]  │ Default is no  │ Type y and press Enter │ Press Enter            │
    # ╰───┴────────┴────────────────┴────────────────────────┴────────────────────────╯
    #
    if (I18N["lang"] == "zh") {
        print sty["blue"] "关于 " coloured_y_n(true) ":"
        print sty["blue"] "關於 " coloured_y_n(false) ":"
        

        print sty["cn"] "╭───┬" sty["bu"] "───────┬" sty["yl"] "───────────┬" sty["cn"] "──────────────────┬" sty["mn"] "───────────────────╮"
        print sty["cn"] "│ # │" sty["bu"] " "amap["option", I18N["lang"], I18N["script"]]"  │" sty["yl"] "   " amap["desc", I18N["lang"], I18N["script"]]"    │" sty["cn"] "       yes        │" sty["mn"] "        no         │"
        print sty["cn"] "├───┼" sty["bu"] "───────┼" sty["yl"] "───────────┼" sty["cn"] "──────────────────┼" sty["mn"] "───────────────────┤"
        print sty["cn"] "│ 0 │" sty["bu"] " " coloured_y_n(true) " │" sty["gn"] " " amap["default_is_yes", I18N["lang"], I18N["script"]] sty["yl"] " │" sty["gn"] " " amap["press_enter", I18N["lang"], I18N["script"]] sty["cn"]"     │" sty["mn"] " " amap["type_n_press_enter", I18N["lang"], I18N["script"]] "  │"
        print sty["cn"] "│ 1 │" sty["bu"] " " coloured_y_n(false) " │" sty["yl"] " " amap["default_is_no", I18N["lang"], I18N["script"]] sty["yl"] "  │" sty["mn"] " " amap["type_y_press_enter", I18N["lang"], I18N["script"]] sty["cn"]" │" sty["gn"] " " amap["press_enter", I18N["lang"], I18N["script"]] sty["mn"]"      │"
        print sty["cn"] "╰───┴" sty["bu"] "───────┴" sty["yl"] "───────────┴" sty["cn"] "──────────────────┴" sty["mn"] "───────────────────╯"
    } else {
        print sty["cyan"] "About " coloured_y_n(true) " & " coloured_y_n(false) ":"
        print sty["cn"] "╭───┬" sty["bu"] "────────┬" sty["yl"] "───────────────┬" sty["cn"] "───────────────┬" sty["mn"] "──────────────╮"
        print sty["cn"] "│ # │" sty["bu"] " Options│" sty["yl"] "  Description  │" sty["cn"] "      yes      │" sty["mn"] "      no      │"
        print sty["cn"] "├───┼" sty["bu"] "────────┼" sty["yl"] "───────────────┼" sty["cn"] "───────────────┼" sty["mn"] "──────────────┤"
        print sty["cn"] "│ 0 │" sty["bu"] " " coloured_y_n(true) "  │" sty["gn"] " Default is    │" sty["gn"] " Press Enter   │" sty["mn"] " Type n and   │"
        print sty["cn"] "│   │" sty["bu"] "        │" sty["gn"] " yes           │" sty["cn"] "               │" sty["mn"] " press Enter  │"
        print sty["cn"] "│ 1 │" sty["bu"] " " coloured_y_n(false) "  │" sty["yl"] " Default is no │" sty["mn"] " Type y and    │" sty["gn"] " Press Enter  │"
        print sty["cn"] "│   │" sty["bu"] "        │" sty["yl"] "               │" sty["mn"] " press Enter   │" sty["mn"] "              │"
        print sty["cn"] "╰───┴" sty["bu"] "────────┴" sty["yl"] "───────────────┴" sty["cn"] "───────────────┴" sty["mn"] "──────────────╯"
    }
}
function coloured_y_n(y) {
    if (y)
        return sty["0"] sty["cn"] "[" sty["gn"] sty["b"] sty["u"] "Y" sty["/u"] sty["/b"] sty["bu"] sty["faint"] "/" sty["/b"] sty["_"] sty["mn"] "n" sty["/_"] sty["cn"] "]" sty["0"]
    else
        return sty["0"] sty["cn"] "[" sty["mn"] sty["_"] "y" sty["/_"] sty["bu"] sty["faint"] "/" sty["/b"] sty["gn"] sty["b"] sty["u"] "N" sty["/u"] sty["/b"] sty["cn"] "]" sty["0"]
}
# -----------
function install_init(_, home_dir, dl_status, file) {
    # net_node: gitee, github
    # -v lang=zh-cn \
    # -v net_node="gh" \
    # -v tmp_dir="/tmp" \

    home_dir = ENVIRON["HOME"]

    if (is_empty(home_dir))
        home_dir = ENVIRON["HOMEPATH"]
    dir_name = ".aidboot"
    GIT["install"] = "install.sh"
    GIT["dir"] = home_dir "/" dir_name


    # /root/.aidboot/install.sh
    file = GIT["dir"] "/" GIT["install"]

    execute(file)

    print_sys_info()
    about_y_or_n()

    if (!do_you_want_to_continue(true))
        exit 0

    set_git_uri(GIT, URI)

    # default
    TMP["file"] = NONE
    set_tmp_dir(TMP, home_dir)

    print sty["yl"] TMP["file"] "\n"
    dl_status = get_file(URI["current"], URI["fallback"], TMP["file"])
    # print_bool(dl_status())
    if (dl_status) {
        execute(TMP["file"])
        exit 0
    } else {
        exit 1
    }
}
# -----------
function print_sys_info() {
    print sty["b"] sty["cn"] "OS info:" sty[0]
    for (i in OS) {
        if (!is_empty(OS[i]))
            print sty["_"] sty["bu"] i ":", sty["/_"] sty["yl"] OS[i]
    }
    print "\n" sty["b"] sty["cn"] "Architecture:" sty[0]
    for (i in ARCH)
        print sty["_"] sty["gn"] i ":", sty["/_"] sty["yl"] ARCH[i]

    print "\n" sty["b"] sty["cn"] "Locale:" sty[0]
    for (i in I18N)
        print sty["_"] sty["mn"] i ":", sty["/_"] sty["yl"] I18N[i]

    print "\n"
}
# -----------
# do_you_want_to_continue(b: bool) -> bool
function do_you_want_to_continue(b, _, opt, a_map, y_n, language) {

    language = I18N["lang"]
    a_map["ext", language] = NONE

    # English
    a_map["do_you", "en"] = "Do you want to" SPACE
    a_map["cont", "en"] = "continue?"

    # Español
    a_map["do_you", "es"] = "¿Quieres "
    a_map["cont", "es"] = "continuar?"

    # Português
    a_map["ext", "pt"] = "region"
    a_map["fallback", "pt"] = "pt"
    # Portugal
    a_map["do_you", "pt" "pt"] = "Quer "
    a_map["cont", "pt" "pt"] = "continuar?"
    # Brasil
    a_map["do_you", "pt" "br"] = "Você quer "

    # Deutsch
    a_map["do_you", "de"] = "Möchten Sie "
    a_map["cont", "de"] = "fortfahren?"

    # polski
    a_map["do_you", "pl"] = "Czy chcesz "
    a_map["cont", "pl"] = "kontynuować?"

    # 日本語
    a_map["cont", "ja"] = "継続"
    a_map["do_you", "ja"] = "しますか?"

    # 한국어
    a_map["cont", "ko"] = "계속하"
    a_map["do_you", "ko"] = "시겠습니까?"

    # čeština
    a_map["do_you", "cs"] = "Chcete "
    a_map["cont", "cs"] = "pokračovat?"
    
    # eesti
    a_map["do_you", "et"] = "Kas soovite "
    a_map["cont", "et"] = "jätkata?"

    # български
    a_map["do_you", "bg"] = "Искате ли да "
    a_map["cont", "bg"] = "продължите?"

    # dansk
    a_map["do_you", "da"] = "Ønsker du at "
    a_map["cont", "da"] = "fortsætte?"

    # latviešu
    a_map["do_you", "lv"] = "Vai vēlaties "
    a_map["cont", "lv"] = "turpināt?"

    # română
    a_map["do_you", "ro"] = "Vrei să "
    a_map["cont", "ro"] = "continui?"

    # magyar 
    a_map["do_you", "hu"] = "Akarod "
    a_map["cont", "hu"] = "folytatni?"

    # Tiếng Việt
    a_map["do_you", "vi"] = "Bạn có muốn "
    a_map["cont", "vi"] = "tiếp tục?"

    # Indonesia
    a_map["do_you", "id"] = "Apakah Anda ingin "
    a_map["cont", "id"] = "melanjutkan?"

    # українська
    a_map["do_you", "uk"] = "Ви хочете "
    a_map["cont", "uk"] = "продовжити?"
    
    # ру́сский язы́к
    a_map["do_you", "ru"] = "Вы хотите "
    a_map["cont", "ru"] = "продолжить?"

    # français
    a_map["do_you", "fr"] = "Voulez-vous "
    a_map["cont", "fr"] = "continuer?"
    
    # বাংলা
    a_map["do_you", "bn"] = "আপনি কি "
    a_map["cont", "bn"] = "অবিরত করতে চান?"

    # italiano
    a_map["do_you", "it"] = "Vuoi "
    a_map["cont", "it"] = "continuare?"

    # svenska
    a_map["do_you", "sv"] = "Vill du "
    a_map["cont", "sv"] = "fortsätta?"

    # suomi
    a_map["do_you", "fi"] = "Haluatko "
    a_map["cont", "fi"] = "jatkaa?"

    # Nederlands
    a_map["do_you", "nl"] = "Wil je "
    a_map["cont", "nl"] = "doorgaan?"

    # slovenčina
    a_map["do_you", "sk"] = "Chcete "
    a_map["cont", "sk"] = "pokračovať?"

    # slovenščina
    a_map["do_you", "sl"] = "Ali želite "
    a_map["cont", "sl"] = "nadaljevati?"

    # Türkçe
    a_map["cont", "tr"] = "Devam "
    a_map["do_you", "tr"] = "etmek istiyor musun?"
    
    # Ελληνικά
    a_map["do_you", "el"] = "Θέλετε να "
    a_map["cont", "el"] = "συνεχίσετε;"

    # 中文
    a_map["ext", "zh"] = "script"
    a_map["fallback", "zh"] = "hans"
    a_map["do_you", "zh" "hans"] = "您要"
    a_map["cont", "zh" "hant"] = "繼續嗎?"
    a_map["cont", "zh" "hans"] = "继续吗?"

    y_n = coloured_y_n(b)

    # This message should not be printed in i18n, but in English.
    # print sty["bu"] a_map["do_you", "en"] sty["yl"] a_map["cont", "en"], y_n

    # print i18n data
    # if (language != "en" && (!is_empty(get_i18n("do_you", a_map))))
    # Now, if the relevant data does not exist, it will automatically fallback

    if (language ~ /ja|ko|tr/)
        print sty["yl"] get_i18n("cont", a_map) sty["bu"] get_i18n("do_you", a_map), y_n
    else
        print sty["bu"] get_i18n("do_you", a_map) sty["yl"] get_i18n("cont", a_map), y_n

    opt = read_to_string(STDIN)
    if (b)
        return (opt ~ /^n|^N/)? false: true
    else
        return (opt ~ /^y|^Y/)? true: false
}
# -----------
# Get i18n data
#
# Example
#
# ```awk
#     # set lang ext, of course, it can be empty.
#     # You can set it to "region" or "script"
#     a_map["ext", "en"] = "region"
#
#     # Basic structure: [key, language ext]
#     a_map["clr", "en" "gb"] = "colour"
#     a_map["clr", "en" "us"] = "color"
#     # Great, we've set up gb and us.
#
#     # Let's see how get_i18n() works!
#     key = "clr"
#     value = get_i18n(key, a_map)
#     print value
#     # If your region is the GB, then it will print the word "colour"
#
#     # Q: But my region is au, and we haven't set it up yet. 
#     #    What should I do?
#     # A: You need to set the fallback.
#
#     # More commonly, you can think of it as having a default value stored inside.
#     a_map["fallback", "en"] = "us"
#
#     # If a_map["clr", "en" "au"] is empty, then it will get a_map["clr", "en" "us"]
#     # Note: Prerequisite is a call to the get_i18n()
#
#     # We can set a fallback for each language
#     # e.g. a_map["fallback", "de"] = "de"
#
#     # You can even set fallback for specific regional languages
#     # e.g. Fallback for ca is us, and fallback for hk is gb
#     a_map["fallback", "en" "ca"] = "us"
#     a_map["fallback", "en" "hk"] = "gb"
# ```
# 
function get_i18n(key, a_map, _, ext, language, value, fallback, region, fallback2, fallback0, map_ext) {
    language = I18N["lang"]

    map_ext = a_map["ext", language]
    ext = a_map["ext"] = is_empty(map_ext)? NONE: I18N[map_ext]

    value = a_map[key, language ext]

    # Priority: fallback2 > fallback > fallback0
    # [[fallback2, fallback, fallback0]; ["zh my => zh sg", "zh => zh cn", "zh cn => en"], ["If the key is pineapple. MY cannot find the value corresponding to this key, then it will fall back to SG. If found, return '黄梨'. If not found, fallback2 => fallback", "If fallback is cn for the zh language. hk => cn, mo => cn, tw => cn. If fallback2 has no value, then it is '菠萝' in fallback. If fallback has no value, fallback => fallback0", "At fallback0 there is no way back. Here, it is neither '黄梨' nor '菠萝', but pineapple"] ]
    #
    # ╭───┬─────────────────────┬────────────────────┬────────────────────╮
    # │ # │      fallback2      │      fallback      │     fallback0      │
    # ├───┼─────────────────────┼────────────────────┼────────────────────┤
    # │ 0 │ zh my => zh sg      │ zh => zh cn        │ zh cn => en        │
    # │ 1 │ If the key is       │ If fallback is cn  │ At fallback0 there │
    # │   │ pineapple. MY       │ for the zh         │ is no way back.    │
    # │   │ cannot find the     │ language. hk =>    │ Here, it is        │
    # │   │ value corresponding │ cn, mo => cn, tw   │ neither '黄梨' nor │
    # │   │ to this key, then   │ => cn. If          │ '菠萝', but        │
    # │   │ it will fall back   │ fallback2 has no   │ pineapple          │
    # │   │ to SG. If found,    │ value, then it is  │                    │
    # │   │ return '黄梨'. If   │ '菠萝' in          │                    │
    # │   │ not found,          │ fallback. If       │                    │
    # │   │ fallback2 =>        │ fallback has no    │                    │
    # │   │ fallback            │ value, fallback => │                    │
    # │   │                     │ fallback0          │                    │
    # ╰───┴─────────────────────┴────────────────────┴────────────────────╯
    #
    if (is_empty(value)) {
        fallback = a_map["fallback", language]
        region = I18N["region"]
        fallback2 = a_map["fallback", language region]
        fallback0 = a_map["fallback"]

        if (!is_empty(fallback2)) {
            value = a_map[key, language fallback2]
            if (!is_empty(value))
                return value
        }

        if (!is_empty(fallback)) {
            value = a_map[key, language fallback]
            if (!is_empty(value))
                return value
        }

        if (is_empty(fallback0))
            fallback0 = "en"

        return a_map[key, fallback0]
    }
    return value
}
# -----------
function execute(file) {
    if (!check_cmd("bash")) {
        print sty["mn"] "Unfortunately, the new edition is not out yet.\n",
        sty["yl"] "The old edition depends on bash, which I think was a mistake.\n",
        sty["gn"] "Let's look forward to the new edition! See you soon."
        exit 1
    }


    if (is_file_exists(file)) {
        if (!(match(read_to_string(file), /TUI_BIN/))) {
            git_dir = GIT["dir"]
            if (match(git_dir, dir_name) && is_file_exists(git_dir)) {
                print "You have already installed an older version, which has serious problems and will be removed automatically. You will need to re-run this awk file."

                print "Press enter to remove the old git dir"

                rm_cmd = "rm -rfv" SPACE git_dir
                print sty["rd"] rm_cmd sty[0]
                if (do_you_want_to_continue(true)) {
                    system(rm_cmd)
                    print sty["mn"] "Please re-run this awk file" sty[0]
                }
                exit 1
            }
        }
        if (system("bash" SPACE file))
            exit 1
        else
            exit 0
    }
}
# -----------
function set_tmp_dir(tmp, home, _, tmp_env, cache_dir, tmp_arr, tmp_arr_len) {
    if (check_cmd("mktemp")) {
        tmp["file"] = get_cmd_out("mktemp")
        tmp_arr_len = split(tmp["file"], tmp_arr, "/")
        tmp["file0"] = tmp_arr[tmp_arr_len]
        for (i = 2; i<= (tmp_arr_len - 1); ++i)
            tmp["dir"] = (i == 2)? "/" tmp_arr[i]: tmp["dir"] "/" tmp_arr[i]
    } else {
        tmp_env = ENVIRON["TMPDIR"]
        cache_dir = home "/.cache"
        tmp["dir"] = (!is_empty(tmp_env))? tmp_env:     (is_file_exists("/tmp"))? "/tmp":    cache_dir

        if (tmp["dir"] == cache_dir && (!is_file_exists(cache_dir))) {
            if (system("mkdir -p" SPACE cache_dir)) {
                print sty["red"] "Error!", sty["yellow"] "Since the", sty["blue"] "TMPDIR" sty["yellow"] "environment variable does not exist, the temporary directory is", sty["green"] cache_dir sty["magenta"] ", but it failed to be created." sty[0]

                exit 1
            }
        }
        tmp["file0"] = ".old-version.sh"
        tmp["file"] = tmp["dir"] "/" tmp["file0"]
    }

    # awk -f ./a.awk -v tmp_dir=/tmp
    # `tmp_dir` from `-v` arg
    if (!is_empty(tmp_dir) && tmp_dir ~ "/") {
        tmp["dir"] = tmp_dir
        tmp["file"] = (tmp_dir ~ "/$")? tmp["dir"] tmp["file0"]:        tmp["dir"] "/" tmp["file0"]
        # print "tmp_file:" tmp["file"]
    }
}
# -----------
function get_file(uri, fallback, file) {
    if (is_empty(DOWNLOADER)) {
        print sty["red"] "Error!" sty["mn"] "No compatible downloader found" > STDERR
        print sty["bu"] "Unfortunately, we did not find curl or wget on your system."
        exit 1
    }

    if (DOWNLOADER == "curl") {
        # If the exit status of the program is true, the loop is exited.
        for (i = 0; i < 30; ++i) {
            # !system => !(0) => true
            if (!system("curl --compressed -L" SPACE uri SPACE "-o" SPACE file) > 0) {
                return true
                # break
            } else {
                print sty["red"] "Unknown curl Error! " > STDERR
                print sty["yl"] "Trying again..." sty[0]
                uri = fallback
            }
        }
    }

    if (DOWNLOADER == "wget") {
        # !system => !(0) => true
        for (i = 0; i < 20; ++i) {
            if (!system("wget" SPACE uri SPACE "-O" SPACE file) > 0) {
                return true
                # break
            } else {
                print sty["red"] "wget is downloading files with errors, please check your ca-certificates." > STDERR
                print sty["blue"] "The manual fix is a better solution than `-no-check-certificate`."
                print sty["yl"] "Trying again..." sty[0]
                uri = fallback
            }
        }
    }

    # (is_empty(downloader)): 
    return false
}
# -----------
# Check if the command exists with `WHICH_CMD`.
#
# [["Cmd Name", Usage, Result]; [uname, 'check_cmd("uname")', "true or false"]]
#
# ╭───┬──────────┬────────────────────┬───────────────╮
# │ # │ Cmd Name │       Usage        │    Result     │
# ├───┼──────────┼────────────────────┼───────────────┤
# │ 0 │ uname    │ check_cmd("uname") │ true or false │
# ╰───┴──────────┴────────────────────┴───────────────╯
#
# What is the difference between check_cmd() and is_cmd_exists()?
#
# `A` and `B` are the former and `C` is the latter.
# A: check_cmd(n) => is_cmd_exists("command", ["-v", n]) -> bool
# B: check_cmd(n) => is_cmd_exists("which", [n]) -> bool
# C: is_cmd_exists(cmd, arg) -> bool
#
# Example
#
# ```awk
#    WHICH_CMD = get_which_cmd()
#    s = check_cmd("uname")
#    if (s)
#       print "Congratulations, `uname` already exists in your system"
#
#    u = check_cmd("unknown_cmd", "--help")
#    print_bool(u)
#    # stdout: false
# ```
#
# opt_arg is optional, and it is not an array, but a string
# If the value is "-d,2,-b,3", and it will automatically slice into an array: 
# ["-d", "2", "-b", "3"]
# i.e. arg[1] = "-d", arg[2] = "2"
# Note: It starts at 1, not 0
#
# check_cmd(cmd: string, opt_arg: string) -> bool
function check_cmd(cmd_name, opt_arg, _, cmd, arr_len, arg) {
    # Is there a constant `WHICH_CMD`?
    # cmd = 
    #   No => cmd_name (it comes from the parameter)
    #   Yes, but opt_arg is not empty => cmd_name
    #   Yes => WHICH_CMD (and cmd_name => arg)
    cmd = (is_empty(WHICH_CMD))? cmd_name: WHICH_CMD

# [[Cmd, Arg]; [command, "-v cmd_name"] [which, cmd_name] [type, cmd_name] [cmd_name, opt_arg]]
#
# ╭───┬──────────┬─────────────╮
# │ # │   Cmd    │     Arg     │
# ├───┼──────────┼─────────────┤
# │ 0 │ command  │ -v cmd_name │
# │ 1 │ which    │ cmd_name    │
# │ 2 │ type     │ cmd_name    │
# │ 3 │ cmd_name │ opt_arg     │
# ╰───┴──────────┴─────────────╯
#
    arr_len = 0
    # If opt_arg is empty and WHICH_CMD is not empty, then set the arg. e.g. (command, ["-v", cmd_name]), (type, [cmd_name])
    if (is_empty(opt_arg)) {
        opt_arg = "--help"
        # set arg(array).
        if (WHICH_CMD == "command")
            arr_len = split("-v" SPACE cmd_name, arg, SPACE)
        else if (WHICH_CMD ~ /type|which/)
            arr_len = split(cmd_name, arg, ",")
    }

    # An implicit condition is included here (!is_empty(opt_arg))
    # i.e. opt_arg is not empty and arr_len is 0
    # [[Var, Value, Empty]; [arr_len, 0, false] [opt_arg, "?", false]]
# ╭───┬─────────┬───────┬───────╮
# │ # │   Var   │ Value │ Empty │
# ├───┼─────────┼───────┼───────┤
# │ 0 │ arr_len │ 0     │ false │
# │ 1 │ opt_arg │ ?     │ false │
# ╰───┴─────────┴───────┴───────╯
#
    if (arr_len == 0) {
        # The cmd variable will be forced to be overwritten.
        cmd = cmd_name
        arr_len = split(opt_arg, arg, ",")
    }

    # if arr_len is still 0, then there is an error.
    if (arr_len == 0) {
        print sty["red"] "Error!\n",
        sty["yellow"] "{\n",
        sty["magenta"] "Name:", sty["cyan"] "check_cmd()\n",
        sty["magenta"] "Reason:", sty["cyan"] "The arr_len is 0.\n",
        sty["magenta"] "Analysis:", sty["cyan"] "When you call check_cmd(), you may have passed in the wrong arguments.\n",
        sty["yellow"] "}" sty[0] > STDERR
        exit 1
    }

    # print "debug::check_cmd", "cmd_name:", cmd_name, "opt_arg:", opt_arg
    # print "debug::check_cmd", "cmd:", cmd, "arg[1]:", arg[1], "arg[2]", arg[2]

    return is_cmd_exists(cmd, arg, arr_len)
}
# -----------
# Use `!system()` to determine if the command exists.
#
# Compared to `check_cmd()`, `is_cmd_exists()` is more low-level.
#
# Some special awk distributions do not have `length(array)`
# So, `arr_len` is used here.
#
# is_cmd_exists(cmd: string, arg: array, arg_len: usize) -> bool
function is_cmd_exists(cmd, arg, arr_len, _, i) {
    if (arr_len >= 1) {
        # Since `split()` is used, it starts at 1 instead of 0 (i = 0; i < arg_len)
        for (i = 1; i <= arr_len; ++i) {
            # print "debug::is_cmd_exists, cmd: ", cmd, "arg: ", arg[i]
            cmd = cmd SPACE arg[i]
        }
    }
    # It's (!system()) not (system())
    return (!system(cmd TO_NULL))
}
# -----------
# [[Name]; [command] [type] [which]]
#
# ╭───┬─────────╮
# │ # │  Name   │
# ├───┼─────────┤
# │ 0 │ command │
# │ 1 │ type    │
# │ 2 │ which   │
# ╰───┴─────────╯
#
function get_which_cmd(_, i, cmd, arg, arr_len, cmd_arr1, cmd_arr1_len, cmd_arr2) {

    cmd_arr1_len = split("command,type,which", cmd_arr1, ",") 

    # The value of cmd_arr1 becomes the key of cmd_arr2. This is an intentional design.
    cmd_arr2[cmd_arr1[1]] = "-v,command"

    # Assign value to cmd_arr2
    for (i = 2; i <= cmd_arr1_len; ++i) 
        cmd_arr2[cmd_arr1[i]] = cmd_arr1[i]

    # Generate a new array based on cmd_arr2 and call `is_cmd_exists()`
    # [[Cmd, Arg]; [command, "-v command"] [type, type] [which, which]]
# 
# ╭───┬─────────┬────────────╮
# │ # │   Cmd   │    Arg     │
# ├───┼─────────┼────────────┤
# │ 0 │ command │ -v command │
# │ 1 │ type    │ type       │
# │ 2 │ which   │ which      │
# ╰───┴─────────┴────────────╯
#
#  Note: if (command -v command) has no errors, then return `command`, otherwise check `type` and `which`
#
    for (i = 1; i <= cmd_arr1_len; ++i) {
        cmd = cmd_arr1[i]

        arr_len = split(cmd_arr2[cmd], arg, ",")
        # print "debug::get_which_cmd, cmd: ", cmd, "arg[1]: ", arg[1]
            if (is_cmd_exists(cmd, arg, arr_len)) {
                return cmd
                # break
            }
    }
    # !(command|type|which):
    return NONE
}
# -----------
# Use the output of the command as the return value
#
# Example
#
# ```awk
#     b = get_cmd_out("mawk,-W,version")
#     print b
#     # stdout: mawk 1.3.4 20200120
# ```
#
# get_cmd_out(full_cmd: string) -> string
function get_cmd_out(full_cmd, _, arr, arr_len, out, cmd, i, v) {
    arr_len = split(full_cmd, arr, ",")

    for (i = 1; i <= arr_len; ++i)
        cmd = cmd SPACE arr[i]

    for (i = 0; (cmd | getline v) > 0; ++i)
        out = (i != 0)? out "\n" v:  v

    close(cmd)
    return out
}
# -----------
# Match the output of the command, and parse
#
# This is read on a row-by-row basis and if a match is made, subsequent content is not parsed further.
# If you want to parse all rows, use `collect_to_arr()`. 
# Note: `parse_cmd_out()` will return a string, but `collect_to_arr()` will collect the parsed contents into an array.
#
# Example
#
# ```awk
#     # arg1: full_cmd(comma separated), arg2: regex_str, arg3: field_separator
#
#     ver = parse_cmd_out("dpkg-query,-s,gpgv", "^(V|v)er.*ion", ":" SPACE)
#     print ver
#     # stdout: 2.2.35-3

#     # The source data are as follows:
#     # Package: gpgv
#     # Status: install ok installed
#     # Section: utils
#     # Source: gnupg2
#     # Version: 2.2.35-3
# ```
#
# parse_cmd_out(full_cmd: string, regex_str: string, field_separator: string) -> string
function parse_cmd_out(full_cmd, regex_str, field_separator, _, arr, arr_len, out, cmd, i) {

    # print "debug::parse_cmd_out,", "regex:", regex_str

    arr_len = split(full_cmd, arr, ",")

    for (i = 1; i <= arr_len; ++i)
        cmd = cmd SPACE arr[i]

    # Set FS, and `getline` will use it
    old_fs = FS
    FS = field_separator

    while ((cmd | getline) > 0) {
        if ($1 ~ regex_str) {
            out = $2
            break
        }
    }

    FS = old_fs

    close(cmd)
    return out
}
# -----------
# Get data from stdin or text, and return string.
#
# If `f` is standard input (stdin) data, then this fn will automatically trim the extra spaces on the left and right sides, and return it.
# If `f` is a file, then this fn will collect it to a string.
#
# Example
#
# ```awk
#     b = read_to_string("/etc/os-release")
#     print b
# ```
#
# input(from stdin/file) -> output(string)
function read_to_string(f, _, v, i, out) {
    if (f == STDIN) {
        # print "Please input a string"
        if ((getline out < f) <= 0) {
            print "IO Error, unknown input data" > STDERR
            exit 1
        }
    } else {
        for (i = 0; (getline v < f) > 0; ++i)
            out = (i != 0)? out "\n" v:  v

        # Do not `close("/dev/null")` manually. In mawk(1.3.4 20200120), it gets a segmentation fault.
        close(f)
    }

    return (f == STDIN)? trim(out): out
}
# -----------
# Parsing text data and collecting data into array.
#
# If "a.txt" has "Key" = "Value", "aa" = "bb".
# After using `collect_to_arr("a.txt", "=", a_map)`
# array: a_map["Key"] = "Value", a_map["aa"] = "bb"
#
# opt_ignore_str (regex str) is an optional argument.
# You can exclude specific string.
# Excluded string will not be parsed.
# 
# Example
#
# ```awk
#     collect_to_arr("/etc/os-release", "=", release)
#
#     for (i in release)
#         print "k:", i, "v:", release[i]
# ```
#
function collect_to_arr(file, field_separator, arr, opt_ignore_str, _, i) {

    # Set FS, and `getline` will use it.
    old_fs = FS
    FS = field_separator

    for (i = 0; (getline < file) > 0; ++i) {
        if (!is_empty(opt_ignore_str) && match($0, opt_ignore_str))
            ""
        else if (match($0, FS))
            arr[$1] = $2
        else
            arr[i] = $0
    }
    FS = old_fs

    #[test]
    # for (i in arr) {
    #     print "k =", i
    #     print "v =", arr[i]
    #     print "------------"
    # }

    close(file)
}
# -----------
# Set ANSI escape code
#
# For outputting fancy text to the terminal.
# When you call `print()` or `printf()`, you can print coloured text or special effects
#
# [[Colour, Foregroud, Backgroud]; [black,30,40] [red, 31, 41] [green, 32, 42] [yellow, 33, 43] [blue, 34, 44] [magenta, 35, 45] [cyan, 36, 46] [white, 37, 47] [default, 39, 49]]
#
# ╭───┬─────────┬───────────┬───────────╮
# │ # │ Colour  │ Foregroud │ Backgroud │
# ├───┼─────────┼───────────┼───────────┤
# │ 0 │ black   │        30 │        40 │
# │ 1 │ red     │        31 │        41 │
# │ 2 │ green   │        32 │        42 │
# │ 3 │ yellow  │        33 │        43 │
# │ 4 │ blue    │        34 │        44 │
# │ 5 │ magenta │        35 │        45 │
# │ 6 │ cyan    │        36 │        46 │
# │ 7 │ white   │        37 │        47 │
# │ 8 │ default │        39 │        49 │
# ╰───┴─────────┴───────────┴───────────╯
#
# Example
#
# ```awk
#     set_ansi_style()
#     print sty["green"] sty["~"] "hello", sty[0] sty["_"] sty["u"] sty["red"] "world" sty[0]
# ```
#
function set_ansi_style(_, colour_arr1, colour_arr1_len, i, colour_arr2) {
    colour_arr1_len = split("black,red,green,yellow,blue,magenta,cyan,white", colour_arr1, ",") 
    # "black,red,green,yellow,blue,magenta,cyan,white"
    # "bk,rd,gn,yl,bu,mn,cn,wh"
    # [[Colour, Abbr]; [black, bk],[red, rd] [green, gn], [yellow, yl],[blue, bu], [magenta, mn], [cyan, cn], [white, wh]]
    #
    # ╭───┬─────────┬──────╮
    # │ # │ Colour  │ Abbr │
    # ├───┼─────────┼──────┤
    # │ 0 │ black   │ bk   │
    # │ 1 │ red     │ rd   │
    # │ 2 │ green   │ gn   │
    # │ 3 │ yellow  │ yl   │
    # │ 4 │ blue    │ bu   │
    # │ 5 │ magenta │ mn   │
    # │ 6 │ cyan    │ cn   │
    # │ 7 │ white   │ wh   │
    # ╰───┴─────────┴──────╯
    #
    # note: sty["bk"] = sty["black"]
    split("bk,rd,gn,yl,bu,mn,cn,wh", colour_arr2, ",") 

    for (i = 1; i <= colour_arr1_len; ++i) {
        # note: Since i starts at 1, this is 29, not 30.
        # print "debug::set_ansi_style", "\33[" (29 + i) "m" 
        sty[colour_arr1[i]] = sty[colour_arr2[i]] = "\33[" (29 + i) "m"
    }


    # default foreground colour
    sty["default"] = sty["de"] = "\33[39m"

    # reset
    sty[0] = "\33[0m"

    # increased intensity 
    sty[1] = sty["bold"] = sty["b"] = "\33[1m"
    # decreased intensity
    sty[2] = sty["faint"] = "\33[2m"
    # neither bold nor faint
    sty["/b"] ="\33[22m"


    sty["italic"] = sty["_"] = "\33[3m"
    # Neither italic, nor blackletter 
    sty["/_"] = "\33[23m"


    sty["strike"] = sty["~"] = "\33[9m"
    # Not striked(crossed out)
    sty["/~"] = "\33[29m"

    sty["underline"] = sty["u"] = "\33[4m"
    sty["not underlined"] = sty["/u"] = "\33[24m"

    sty["background"] = sty["bg"] = "\33[7m"
    sty["foreground"] = sty["fg"] = "\33[27m"
}
# -----------
# Set constants
#
# About stdin:
# Some systems do not have "/dev/tty" or "/proc/self/fd/0"
# Some awk distros do not support "-"
# Interestingly, for busybox awk on windows (busybox: v1.36.0-FRP-4621-gf3c5e8bc3 (2022-02-28 07:17:58 GMT)), it automatically converts "/dev/stdin" to the windows interface, but neither "/dev/tty" or "-" will work.
# In mawk(1.3.4 20200120), an explicit call to `close("/dev/stdin")` will result in a segmentation fault.
#
function set_const() {
    STDIN = "/dev/stdin"
    NONE = ""
    SPACE = " "

    TO_NULL = " >/dev/null"
    STDERR = "/dev/stderr"

    # true and false are not keywords in the current awk
    false = 0
    true = 1

    # for outputting fancy text to the terminal
    set_ansi_style()

    # Get constant at runtime
    WHICH_CMD = get_which_cmd()
    
    # set downloader
    DOWNLOADER = (check_cmd("curl"))? "curl": (check_cmd("wget"))? "wget": NONE
}
# -----------
# Print true or else , instead of 1 or 0
#
# ╭───┬──────┬───────╮
# │ # │  Ok  │  Err  │
# ├───┼──────┼───────┤
# │   │ true │ false │
# ╰───┴──────┴───────╯
#
# Example
#
# ```awk
#    m = 1
#    print_bool(m)
#    # stdout: true
#
#    n = 0
#    print_bool(n)
#    # stdout: false
#    # note: this is stdout, not stderr
# ```
#
# print_bool(b: bool)
function print_bool(b) {
    print (b)? "true": "false"
}
# -----------
# If the string is empty -> true
# If not empty -> false
#
# Note: If you want to remove spaces or other invisible characters, you need to call `trim()` first
#
# [[s_type, Usage, Result]; ["string", "is_empty(s)", "true or false"]]
#
# ╭───┬────────┬─────────────┬───────────────╮
# │ # │ s_type │    Usage    │    Result     │
# ├───┼────────┼─────────────┼───────────────┤
# │ 0 │ string │ is_empty(s) │ true or false │
# ╰───┴────────┴─────────────┴───────────────╯
#
# Example
#
# ```awk
#     s = ""
#
#     if (is_empty(s))
#         print "Ok, s is empty"
# ```
#
# is_empty(s: string) -> bool
function is_empty(s) {
    # Unfortunately, busybox awk(1.35.0-1) does not have `typeof()`
    ## type: "array", "number", "regexp", "string", "strnum", "unassigned", or "undefined".
    # (typeof(s) ~ /unassigned|undefined/)? true: ...

    # The `length()` of some awk distributions may only support the string type.
    # gawk and mawk also support the array and number types. i.e. They can use `length(array)`
    return (length(s) == 0 || s == NONE)? true : false
}
# -----------
# Trim off whitespace characters on the left and right sides of the string.
# whitespace chars: [ \t\n\r\f\v]
#
# [[Usage, Trim, "Result(a new str)"]; ["trim(\"  aa bb  \")", "whitespace chars(left and right)", "\"aa bb\""]]
#
# ╭───┬───────────────────┬──────────────────────────────────┬───────────────────╮
# │ # │       Usage       │               Trim               │ Result(a new str) │
# ├───┼───────────────────┼──────────────────────────────────┼───────────────────┤
# │ 0 │ trim("  aa bb  ") │ whitespace chars(left and right) │ "aa bb"           │
# ╰───┴───────────────────┴──────────────────────────────────┴───────────────────╯
#
# Example
#
# ```awk
#     s = "  \t \t foo"
#
#     n = trim(s)
#     print n
#     # stdout: foo
#
#     print s
#     # This will output spaces, tabs and foo
# ```
#
# trim(s: string) -> string
function trim(s) {
    gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
    return s
}
# -----------
# Remove the quotation marks on the left and right sides.
#
# Example
# 
# ```awk
#     s = "\"\"awk\"\""
#
#     print s
#     # stdout: ""awk""
#
#     print dequotes(s)
#     # stdout: awk
# ```
#
# dequotes(s: string) -> string
function dequotes(s) {
    gsub(/^("|')+|("|')+$/, NONE, s)
    return s
}
# -----------
# If the file exists -> true
# Otherwise -> false
#
# [[Arg, Usage, Result]; [file, "is_file_exists(file)", "true or false"]]
#
# ╭───┬──────┬──────────────────────┬───────────────╮
# │ # │ Arg  │        Usage         │    Result     │
# ├───┼──────┼──────────────────────┼───────────────┤
# │ 0 │ file │ is_file_exists(file) │ true or false │
# ╰───┴──────┴──────────────────────┴───────────────╯
#
# Example
#
# ```awk
#     file = "/usr/lib/os-release"
#
#     if (is_file_exists(file))
#         printf("Ok, %s exists\n", file)"
#
#
#    f = "/tmp/a_non-existent_file.txt"
#    b = is_file_exists(f)
#
#    print_bool(b)
#    # stdout: false
# ```
#
# is_file_exists(f: string) -> bool
function is_file_exists(f) {
    # it's (!system()), not (system())
    return !system("test -e" SPACE f)
}
# -----------
#[test]
function test_cmd_exists(_, cmd, arg, arr_len, i) {
    cmd = "command"
    arr_len= split("-v type aa bb cc dd 1.2e-3 ff 0x09", arg, SPACE)

    print "arr.len(): " arr_len

    for (i in arg)
        print i, ":", arg[i]
    status = is_cmd_exists(cmd, arg, arr_len)
    print_bool(status)
}
# -----------
#[test]
function test_check_cmd() {
    WHICH_CMD = get_which_cmd()
    print "WHICH_CMD:", WHICH_CMD
    s = check_cmd("uname", "-m")
    print_bool(s)
}
# -----------
#[test]
function test_alice_s_fruit(_, s, fruit, my, alice) {
    s = ""
    fruit = "lychees"

    if (is_empty(s)) {
        print "s is empty\n",
            "OK, I knew there was nothing there."
    }
    # note: my[fruit] is my["lychees"], not my["fruit"]
    my[fruit] = 2

    if (is_empty(my[fruit])) {
        printf("Since the value of `my[fruit]` has already been defined earlier, don't be too sad.\n",
            "The `if` will not go through this branch.\n")
    } else {
        printf("Please note that there is a difference between a variable that is empty and has the value \"\" and a variable that is not empty and has the value 0.\n",
            "But in awk, if an int variable is uninitialized, it will automatically be set to 0.\n",
            "Fortunately, mawk/gawk can call `typeof()` to check the type of a variable. We can determine (unassigned|undefined). Unfortunately, some awk distros do not have `typeof()`\n")

        print (my[fruit] == 0)? "The sad thing is that I don't have any "                               fruit: (my[fruit] == 1)? "Only one, but no bad":                                                                                        "Woo-hoo! It's so exciting. I have many many", fruit
    }

    alice[fruit] = 5

    # note: (!false) = (true)
    if (!is_empty(alice[fruit]) && alice[fruit] > my[fruit])
        printf("Oh, no! Alice has %d %s.\nThis is unfair.\n", alice[fruit], fruit)
}
# -----------
#[test]
function test_file_existence() {
    if (is_file_exists("/usr/lib/os-release"))
        print "Ok, it exists."

    if (!is_file_exists("/etc/lib-release"))
        print "Oh no, it doesn't exist"
}
# -----------
function get_architecture(uname, _, a) {
    if (check_cmd("apt-cache") && check_cmd("dpkg-query"))
        a = get_cmd_out("dpkg,--print-architecture")
    else
        a = get_cmd_out(uname ",-m")

    # global 
    # Key: "llvm", Value: "x86_64"
    # Key: "deb", Value: "amd64"
    ARCH["llvm"] = "x86_64"

    parse_architecture(a, ARCH)
    #[test]
    # print "debug::get_architecture,\n", sty["green"] "llvm_arch:", sty["yellow"], arch["llvm"] "\n",
    # sty["blue"] "deb_arch:", sty["cyan"] arch["deb"]
}
# -----------
function parse_architecture(a, arch) {
    # While gawk supports `switch case` statements, some other `awk` distributions do not.

    # [[llvm_arch, deb_arch]; [x86_64, amd64], [aarch64, arm64], [armv7, "armhf or armel"], [arm, "armel"], [armv5te, armel], [i686, i386], [riscv64gc, riscv64], [powerpc64le, ppc64el], [s390x, s390x], [mips64el, mips64el], [mipsel, mipsel]]

    # note: It doesn't support all architectures.

    # `x86_64` must precede `i386` 
    
    # x86_64|amd64|x64
    if (a ~ /(x(86_)?|amd)64/) {
        arch["deb"] = "amd64"
        arch["llvm"] = "x86_64"

        # aarch64|armv8a|armv9|arm64
    } else if (a ~ /a(arch64|rm(v(8a|9)|64))/) {
        # aarch64-apple-darwin
        # aarch64-linux-android
        # aarch64-pc-windows-msvc
        # aarch64-unknown-linux-musl
        # aarch64-unknown-linux-gnu

        arch["llvm"] = "aarch64"
        arch["deb"] = "arm64"

        # armv7|armv8l|armhf|^arm$
    } else if (a ~ /arm(v(7|8l)|hf)|^arm$/) {
        # armv7-unknown-linux-gnueabihf
        # armv7-unknown-linux-musleabihf

        # android abi: https://developer.android.com/ndk/guides/abis.html
        # armv7-linux-androideabi, armeabi-v7a
        # https://developer.android.com/ndk/guides/cpu-arm-neon
        # thumbv7neon-linux-androideabi, Thumb2-mode ARMv7a Android with NEON

        arch["llvm"] = "armv7"
        arch["deb"] = "armhf"

        # armel|armv6
    } else if (a ~ /arm(el|v6)/) {
        arch["llvm"] = "arm"
        # On some systems, arm-unknown-linux-gnueabihf might be "armhf"
        # note: arm-unknown-linux-gnueabi and arm-unknown-linux-musleabi are "armel"
        arch["deb"] = "armel"

        # To be honest, I don't want to support the armv6 and v5 architectures.
        # Maybe one day I will remove or comment out these `else if`
    } else if (a ~ /arm(el|v5)/) {
        arch["llvm"] = "armv5te"
        arch["deb"] = "armel"
    } else if (a ~ /arm/) {
        # armv4 or older architectures are not supported, if none of the above arm architectures are matched then the default will be to fall back to arm64
        arch["llvm"] = "aarch64"
        arch["deb"] = "arm64"

        # i386 i486 i586 x86 x32 x86_32
    } else if (a ~ /x((86_)?32|86)|i[3-5]86/) {
        # note: On some systems it will output i386 even if it is i686.
        print "It does not support i386 and i486, only i586 and i686"
        arch["llvm"] = "i586"
        arch["deb"] = "i386"
    } else if (a ~ /i686/) {
        arch["llvm"] = "i686"
        arch["deb"] = "i386"

    } else if (a ~ /riscv64/) {
        arch["llvm"] = "riscv64gc"
        arch["deb"] = "riscv64"

    } else if (a ~ /powerpc64(el|le)?|ppc64(el|le)?/) {
        print "It does not support ppc64, only ppc64el"
        arch["llvm"] = "powerpc64le"
        arch["deb"] = "ppc64el"

    } else if (a ~ /s390/) {
        # regex: Use /s390/, not /s390x/

        arch["llvm"] = arch["deb"] = "s390x"

    } else if (a ~ /mips64/) {
        arch["llvm"] = arch["deb"] = "mips64el"

    } else if (a ~ /mips/) {
        # note: On mipsel devices, the output of `uname -m` may not be mipsle/mispel, but mips
        arch["llvm"] = arch["deb"] = "mipsel"

    } else {
        print sty["cyan"]
        print "╭────┬─────────────┬────────────────╮"
        print "│  # │  llvm_arch  │    deb_arch    │"
        print "├────┼─────────────┼────────────────┤"
        print "│  0 │ x86_64      │ amd64          │"
        print "│  1 │ aarch64     │ arm64          │"
        print "│  2 │ armv7       │ armhf or armel │"
        print "│  3 │ arm         │ armel          │"
        print "│  4 │ armv5te     │ armel          │"
        print "│  5 │ i686        │ i386           │"
        print "│  6 │ riscv64gc   │ riscv64        │"
        print "│  7 │ powerpc64le │ ppc64el        │"
        print "│  8 │ s390x       │ s390x          │"
        print "│  9 │ mips64el    │ mips64el       │"
        print "│ 10 │ mipsel      │ mipsel         │"
        print "╰────┴─────────────┴────────────────╯"
        print sty["green"] "Note: There is a difference between", sty["blue"] "arm-unknown-linux-gnueabi", sty["cyan"] "and", sty["yellow"] "arm-unknown-linux-gnueabihf." sty[0]
        print "On some systems, the latter may be", sty["yellow"] "armhf", sty[0] "instead of", sty["blue"] "armel."
        print sty["yellow"] "---------------------"
        # print "Adapting to different architectures is hard work. To be honest, I don't want to be that tired."        
        print sty["red"] "Unfortunately!\n",
        sty["blue"] "It does not support the", sty["magenta"] a, sty["cyan"] "architecture." sty[0] > STDERR
        exit 1
    }
}
# -----------
# Please do not remove the test function.
BEGIN {
    main()
    # test()
}
# -----------
